<template>
  <table
    :border="border ? 1 : 0"
    :style="{
      'border-spacing': borderSpacing,
      // border: borderColor ? '1px solid ' + borderColor : undefined,
    }"
    cellspacing="0"
  >
    <tr
      :style="{
        ...topStyle,
        border: borderColor ? '1px solid ' + borderColor : undefined,
      }"
      v-show="showTop"
    >
      <th  :width="item.width ? item.width : undefined" v-for="item in column" :align="item.align">
        <div v-show="item.headerSlotName">
          <slot :name="`header_${item.headerSlotName}`" :row="item"> </slot>
        </div>
        <span
          v-show="!item.headerSlotName"
          
          :style="({ fontWeight: headerFontSize !== undefined ? headerFontSize: 'bold',fontFamily:headerFamily} as CSSProperties)"
        >
          {{ item.title }}
        </span>
      </th>
    </tr>

    <Form
      :validateOnBlur="false"
      :validation-schema="schema"
      v-slot="{ validate, values }"
      as="tr"
      v-for="(item, index) in tableData"
      :style="{
        ...lineStyle,
        border: borderColor ? '1px solid ' + borderColor : undefined,
      }"
      :ref="($event) => setRef($event, index)"
    >
      <td v-for="items in column" :width="items.width ? items.width : undefined"  @click="() => tdClick(index, item)" :align="items.align">
        <div v-show="!items.slotName" :style="tdStyle" class="table-td">
          <span class="line-feed" v-show="!item.editor || items.forbidEdit">{{
            item[items.dataKey]
          }}</span>
          <div v-show="item.editor && !items.forbidEdit">
            <Field
              :validateOnChange="false"
              :validateOnBlur="false"
              as="div"
              v-slot="{ field }"
              :name="items.dataKey"
              class="input"
              v-model="item[items.dataKey]"
            >
              <input v-bind="field" />
              <ErrorMessage
                class="errorMsg"
                :style="errorStyle"
                :name="items.dataKey"
              />
            </Field>
          </div>
        </div>
        <div v-show="items.slotName">
          <slot
            :name="items.slotName"
            :row="(item as any)"
            :index="index"
            :validate="validate"
            :rowValue="values"
          ></slot>
        </div>
      </td>
    </Form>
    <div class="empty-data" v-show="tableData.length === 0">暂无数据</div>
  </table>
</template>
<script setup lang="ts" name="">
import { computed, CSSProperties, ref } from "vue";
import { Column, DataSource } from "./interface";
import { onClickOutside } from "@vueuse/core";
import { Form, Field, ErrorMessage } from "vee-validate";

interface Prop {
  border?: boolean;         //是否显示边框
  showTop?:boolean;         //是否显示头部
  column?: Array<Column>;   //表格头部栏目
  topHeight?: string;       //表格头部高度
  headerFontSize?: number | string; //表格头部字体大小
  topCss?: { [key: string]: any };   //头部css自定义
  headerFamily?:string;            //头部字体
  lineCss?: { [key: string]: any };   //表格一行tr样式
  tdCss?: { [key: string]: any };     //单个表格td 样式
  addField?: { [key: string]: any };  //需要追加的字段
  borderSpacing?: string;               //tr间隔
  borderColor?: string;                //表格颜色
  restDataFunc?: (newData: DataSource, oldData: DataSource) => DataSource;  //开始修改功能时重置数据回调
  dataSource?: Array<DataSource>;                                            //表格数据
  defaultEdit?: boolean;                                                    //默认是否为表格修改模式
  primaryKey?: string;                                             //主键字段
  errorCss?: { [key: string]: any };                                //修改字段时的css
  schema?: Record<string, any> | undefined;                              //yup验证规则
}

const props = withDefaults(defineProps<Prop>(), {
  border: false,
  showTop:true,
  column: () => [],
  dataSource: () => [],
  topCss: () => {
    return {};
  },
  lineCss: () => {
    return {};
  },
  topHeight: "",
  tdCss: () => {
    return {};
  },
  borderColor: "",
  addField: () => {
    return {};
  },
  borderSpacing: "",
  primaryKey: "",
  errorCss: () => {
    return {};
  },
  headerFontSize: undefined,
  restDataFunc: undefined,
  headerFamily:undefined,
  defaultEdit: false,
  schema: undefined,
});

const getRef = (index: number): string | number => {
  let row: DataSource = tableData.value[index];
  return props.primaryKey
    ? row.hasOwnProperty(props.primaryKey)
      ? `${row[props.primaryKey]}`
      : index
    : index;
};
const refs = ref<{ [key: string | number]: any }>([]);

const setRef = (el: any, index: number) => {
  if (tableData.value[index]) refs.value[getRef(index)] = el;
};

const tableData = computed((): Array<DataSource> => {
  return props.dataSource.map((item) => {
    item.editor = item.hasOwnProperty("editor")
      ? item.editor
      : props.defaultEdit;
    for (let key in props.addField)
      item.hasOwnProperty(key) ? item[key] : props.addField[key];
    return item;
  });
});

const topStyle = computed((): { [key: string]: string | undefined } => {
  return Object.assign(
    {
      height: props.topHeight ? props.topHeight : undefined,
    },
    props.topCss
  );
});

const lineStyle = computed((): { [key: string]: string | undefined } => {
  return {
    ...props.lineCss,
  };
});
const errorStyle = computed((): { [key: string]: string | undefined } => {
  return {
    ...props.errorCss,
  };
});
const tdStyle = computed((): { [key: string]: string | undefined } => {
  return {
    ...props.tdCss,
  };
});

let close: (() => void) | undefined = undefined;
const openEditor = (index: number) => {
  let data: DataSource = { ...props.dataSource[index] };
  if (close !== undefined) close();
  close = onClickOutside(refs.value[getRef(index)], () => {
    closeEditor(
      index,
      props.restDataFunc === undefined
        ? data
        : props.restDataFunc(tableData.value[index], data)
    );
  });
  props.dataSource[index].editor = true;
};

//关闭修改行
const closeEditor = (
  index: number,
  restData: undefined | DataSource = undefined
) => {
  if (close !== undefined) close(); //销毁监听
  if (restData) {
    for (let i = 0; i < props.column.length; i++) {
      let dataKey: keyof DataSource = props.column[i].dataKey;
      props.dataSource[index][dataKey] = restData[dataKey];
    }
  }
  props.dataSource[index].editor = false;
  emits("tdEditorClose");
};
const emits = defineEmits<{
  (event: "tdClick", index: number, item: DataSource): void;
  (event: "tdEditorClose"): void;
}>();
const tdClick = (index: number, item: DataSource) => {
  emits("tdClick", index, item);
};

defineExpose({ openEditor, closeEditor });
</script>
<style scoped lang="scss">
@import "./index.scss";
</style>
